package com.pxmeta.user.service.impl;

import cn.hutool.core.collection.CollectionUtil;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.easemob.im.server.EMService;

import com.google.common.collect.Lists;

import com.pxmeta.chat.entity.dto.SetDisturbDTO;
import com.pxmeta.chat.entity.vo.ChatUserVO;
import com.pxmeta.chat.service.HuanxinService;
import com.pxmeta.common.constant.HttpStatus;
import com.pxmeta.common.exception.ServiceException;
import com.pxmeta.common.utils.StringUtils;
import com.pxmeta.common.utils.agora.AgoraClient;
import com.pxmeta.common.utils.sign.Md5Utils;
import com.pxmeta.dynamic.entity.Dynamic;
import com.pxmeta.dynamic.mapper.DynamicMapper;
import com.pxmeta.order.service.IOrderService;
import com.pxmeta.party.entity.Association;
import com.pxmeta.party.entity.PartyUser;
import com.pxmeta.party.entity.enums.UserLiveStatusEnum;
import com.pxmeta.party.entity.vo.ChannelUserVO;
import com.pxmeta.party.service.IAssociationService;
import com.pxmeta.party.service.IPartyUserService;
import com.pxmeta.product.entity.Shop;
import com.pxmeta.product.service.IShopService;
import com.pxmeta.skill.service.IUserSkillService;
import com.pxmeta.user.converter.UserConverter;
import com.pxmeta.user.entity.*;
import com.pxmeta.user.entity.constance.UserConstance;
import com.pxmeta.user.entity.dto.UpdateUserAccountDTO;
import com.pxmeta.user.entity.dto.UpdateUserNumberDTO;
import com.pxmeta.user.entity.dto.UserDTO;
import com.pxmeta.user.entity.vo.*;
import com.pxmeta.user.mapper.UserMapper;
import com.pxmeta.user.service.*;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 用户Service业务层处理
 *
 * @author wcl
 * @date 2022-03-20
 */
@Service
@SuppressWarnings("all")
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private IUserRankService userRankService;

    @Autowired
    private IShopService shopService;

    @Autowired
    private IUserSkillService userSkillService;

    @Autowired
    private HuanxinService huanxinService;

    @Autowired
    private IUserTagService userTagService;

    @Autowired
    private DynamicMapper dynamicMapper;

    @Autowired
    private IUserFollowService userFollowService;

    @Autowired
    private IUserBlockService userBlockService;

    @Autowired
    private IPartyUserService partyUserService;

    @Autowired
    private IUserDisturbService userDisturbService;

    @Autowired
    private IUserStudentService userStudentService;

    @Autowired
    private NearbyUserService nearbyUserService;

    @Autowired
    private IUserVisitService userVisitService;

    @Autowired
    private IUserMatchService userMatchService;

    @Autowired
    private IAssociationService associationService;

    @Autowired
    private IUserPhotoService userPhotoService;

    @Autowired
    private IUserBillService userBillService;

    @Autowired
    private EMService emService;

    @Autowired
    private IOrderService orderService;

    @Autowired
    private AgoraClient agoraClient;


    /**
     * 获取用户信息
     *
     * @param userId
     * @return
     */
    @Override
    public List<UserDTO> getUserInfos(Long currentUserId, List<Long> userIds) {
        if (userIds.isEmpty()){
            return Lists.newLinkedList();
        }
        List<User> users = baseMapper.selectBatchIds(userIds);
        List<PartyUser> partyUsers = partyUserService.getByUserIds(userIds);
        List<UserDTO> finalList = Lists.newLinkedList();
        for (User user : users) {
            UserDTO userDTO = UserConverter.toDto(user);
            LocalDate birthday = user.getBirthday();
            userDTO.setBirthday(user.getBirthday());
            userDTO.setIntro(user.getIntro());
            userDTO.setCreateTime(user.getCreateTime());
            for (PartyUser partyUser : partyUsers) {
                if (partyUser.getUserId().equals(user.getId())){
                    String partyId = partyUser.getPartyId();
                    userDTO.setLiveStatus(UserLiveStatusEnum.NO_LIVE.getStatus());

                    //再校验一次
                    if (StringUtils.isNotBlank(partyId)){
                        ChannelUserVO vo = agoraClient.getUsers(partyId).toJavaObject(ChannelUserVO.class);
                        if (vo.getChannelExist()) {
                            List<String> broadcaseters = vo.getBroadcasters().stream().map(l -> l.toString()).collect(Collectors.toList());
                            List<String> audiences = vo.getAudience().stream().map(l -> l.toString()).collect(Collectors.toList());
                            LinkedList<String> allUsernames = Lists.newLinkedList();
                            allUsernames.addAll(broadcaseters);
                            allUsernames.addAll(audiences);
                            if (allUsernames.contains(user.getUsername())) {
                                userDTO.setLiveStatus(UserLiveStatusEnum.VOICE_LIVE.getStatus());
                                userDTO.setPartyId(partyId);
                            }
                        }
                    }
                    break;
                }
            }
            finalList.add(userDTO);
        }
        return finalList;
    }

    /**
     * 获取用户信息
     *
     * @param userId
     * @return
     */
    @Override
    public UserDTO getUserInfo(Long currentUserId, Long userId) {
        User user = baseMapper.selectById(userId);
        if (null == user) {
            throw new ServiceException("该用户已被注销！", HttpStatus.USER_CANCEL_ERROR);
        }
        Association association = associationService.getUserAssociation(userId);
        UserDTO userDTO = UserConverter.toDto(user);
        userDTO.setAssociation(association);
        Long followCount = userFollowService.getFollowCount(userId);
        userDTO.setFollowCount(followCount);
        Long fansCount = userFollowService.getFansCount(userId);
        userDTO.setFansCount(fansCount);
//        UserStudent student = userStudentService.getStudentInfo(userId);
//        userDTO.setStudentInfo(student);
        Integer visitorNumber = userVisitService.selectSawMeCount(userId);
        userDTO.setVisitorNumber(visitorNumber);
        LocalDate birthday = user.getBirthday();
//        boolean isBlock = userBlockService.isBlock(currentUserId, userId) != null;
//        userDTO.setIsBlock(isBlock);
        userDTO.setBirthday(user.getBirthday());
        userDTO.setSchoolName(user.getSchoolName());
        userDTO.setIntro(user.getIntro());
        Boolean isAccompany  = userSkillService.checkIsAccompany(userId);
        userDTO.setIsAccompany(isAccompany);
        userDTO.setCreateTime(user.getCreateTime());
        List<String> tags = getTagList(user.getTagIds());
        userDTO.setTags(tags);
        userDTO.setInviteCode(user.getInviteCode());
        String partyId = partyUserService.getUserPartyId(userId);
        userDTO.setLiveStatus(UserLiveStatusEnum.NO_LIVE.getStatus());
        UserRank userRank = userRankService.getById(userId);
        List<Shop> userShops = shopService.getUserUsedDecoration(userId);
        userDTO.setUsedDecoration(userShops);
        userDTO.setUserRank(userRank);
        userDTO.setPartyId(partyId);
        String username = userDTO.getUsername();
        List<String> usernames = Collections.singletonList(username);
        return userDTO;
    }


    private List<String> getTagList(String tagIds) {
//        LocalDate birthday = user.getBirthday();
        List<String> tags = Lists.newLinkedList();
//        if (ObjectUtil.isNotNull(birthday)) {
//            LocalDate now = LocalDate.now();
//            int nowYear = now.getYear();
//            int birthdayYear = birthday.getYear();
//            int age = nowYear - birthdayYear;
//
//            int monthValue = birthday.getMonthValue();
//            int dayOfMonth = birthday.getDayOfMonth();
//            int monthDay = monthValue * 100 + dayOfMonth;
//
//            String xingzuo = getXingzuo(monthDay);
//            //获取年龄标签
//            String ageTag = getAgeTag(birthdayYear);
//            if (StringUtils.isNotEmpty(ageTag)) {
//                tags.add(ageTag);
//            }
//            tags.add(xingzuo);
//        }
        if (StringUtils.isNotBlank(tagIds)) {
            List<String> tagIdList = Arrays.asList(tagIds.split(","));
            if (CollectionUtil.isNotEmpty(tagIdList)) {
                List<String> userTags = userTagService.listByIds(tagIdList).stream().limit(5).map(UserTag::getName).collect(Collectors.toList());
                tags.addAll(userTags);
            }
        }
        return tags;
    }

    private String getAgeTag(Integer birthdayYear) {
        String ageTag = "";
        if (birthdayYear >= 2020 && birthdayYear < 2025) {
            ageTag = "20后";
        } else if (birthdayYear >= 2015 && birthdayYear < 2020) {
            ageTag = "15后";
        } else if (birthdayYear >= 2010 && birthdayYear < 2015) {
            ageTag = "10后";
        } else if (birthdayYear >= 2000 && birthdayYear < 2005) {
            ageTag = "00后";
        } else if (birthdayYear >= 1995 && birthdayYear < 2000) {
            ageTag = "95后";
        } else if (birthdayYear >= 1990 && birthdayYear < 1995) {
            ageTag = "90后";
        } else if (birthdayYear >= 1985 && birthdayYear < 1990) {
            ageTag = "85后";
        } else if (birthdayYear >= 1980 && birthdayYear < 1985) {
            ageTag = "80后";
        } else if (birthdayYear >= 1975 && birthdayYear < 1980) {
            ageTag = "75后";
        } else if (birthdayYear >= 1970 && birthdayYear < 1975) {
            ageTag = "70后";
        }
        return ageTag;
    }

    private String getXingzuo(Integer monthday) {
        int month = monthday / 100;
        int day = monthday % 100;
        String xingzuo = "";
        switch (month) {
            case 1:
                xingzuo = day < 21 ? "摩羯座" : "水瓶座";
                break;
            case 2:
                xingzuo = day < 20 ? "水瓶座" : "双鱼座";
                break;
            case 3:
                xingzuo = day < 21 ? "双鱼座" : "白羊座";
                break;
            case 4:
                xingzuo = day < 21 ? "白羊座" : "金牛座";
                break;
            case 5:
                xingzuo = day < 22 ? "金牛座" : "双子座";
                break;
            case 6:
                xingzuo = day < 22 ? "双子座" : "巨蟹座";
                break;
            case 7:
                xingzuo = day < 23 ? "巨蟹座" : "狮子座";
                break;
            case 8:
                xingzuo = day < 24 ? "狮子座" : "处女座";
                break;
            case 9:
                xingzuo = day < 24 ? "处女座" : "天秤座";
                break;
            case 10:
                xingzuo = day < 24 ? "天秤座" : "天蝎座";
                break;
            case 11:
                xingzuo = day < 23 ? "天蝎座" : "射手座";
                break;
            case 12:
                xingzuo = day < 22 ? "射手座" : "摩羯座";
                break;
        }
        return xingzuo;
    }

    private static String getDefaultNickname(){
        String preStr = "犹豫的,悲伤的,快乐的,活泼的,固执的,高兴的,执着的,忧郁的,激情的,兴奋地,难过的,孤独的,迷茫的,愤怒的,忧伤的,时尚的,腼腆的,内向的,开朗的";
        String lastStr = "西瓜,南瓜,二哈,橘子,柯基,荔枝,小柴,旺财,树根,山坡,二蛋,小水,土墩,包子,土豆";


        List<String> prelist = Arrays.asList(preStr.split(","));
        List<String> lastlist = Arrays.asList(lastStr.split(","));

        Collections.shuffle(prelist);
        Collections.shuffle(lastlist);

        String nickname = prelist.get(0) + lastlist.get(0);
        return nickname;
    }



    //todo
    @Override
    public User saveUser(String phone, String password) {
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(User::getUsername, phone);
        User ktUser = baseMapper.selectOne(wrapper);
//        String encodePwd = passwordEncoder.encode(password);
//        if (!passwordEncoder.matches(password,encodePwd)){
//            throw new ServiceException("密码错误！", HttpStatus.UNAUTHORIZED);
//        }
        if (ktUser == null) {
            String username = getUsername();
            User user = new User();
            user.setAvatar("https://pengxinapp.oss-cn-beijing.aliyuncs.com/avatar/Group%203346108%403x.png");
//            user.setNickname(username);
            user.setUsername(username);
            LocalDate date = LocalDate.of(2005, 05, 01);
            user.setBirthday(date);
            user.setNickname(getDefaultNickname());
            user.setIntro("这个人很懒，什么也没写...");
            user.setPhone(phone);
            user.setAccount(username);
            user.setPassword(Md5Utils.hash(password));
            baseMapper.insert(user);


            UserRank userRank = new UserRank();
            userRank.setUserId(user.getId());
            userRankService.save(userRank);

            huanxinService.register(username, Md5Utils.hash(password), username);
            return user;
        }
        return ktUser;
    }


    //todo
    @Override
    @Transactional(rollbackFor = ServiceException.class)
    public User saveUser(String phone, String password, Long parentUserId) {
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(User::getUsername, phone);
        User ktUser = baseMapper.selectOne(wrapper);
//        String encodePwd = passwordEncoder.encode(password);
//        if (!passwordEncoder.matches(password,encodePwd)){
//            throw new ServiceException("密码错误！", HttpStatus.UNAUTHORIZED);
//        }
        if (ktUser == null) {
            String username = getUsername();
            User user = new User();
            user.setParentUserId(parentUserId);
            user.setAvatar("https://pengxinapp.oss-cn-beijing.aliyuncs.com/avatar/Group%203346108%403x.png");
//            user.setNickname(username);
            user.setUsername(username);
            LocalDate date = LocalDate.of(2005, 05, 01);
            user.setBirthday(date);
            user.setNickname(getDefaultNickname());
            user.setIntro("这个人很懒，什么也没写...");
            user.setPhone(phone);
            user.setAccount(username);
            user.setPassword(Md5Utils.hash(password));
            baseMapper.insert(user);

            UserRank userRank = new UserRank();
            userRank.setUserId(user.getId());
            userRankService.save(userRank);
            huanxinService.register(username, Md5Utils.hash(password), username);
            return user;
        }
        return ktUser;
    }


    /**
     * 搭讪
     *
     * @param username 昵称
     * @param sex      性别
     */
    @Override
    public void accost(String username, Integer sex) {
        List<String> list = Lists.newLinkedList();
        //妹子，男生搭讪
        if (sex == 2) {
            list.add("2");
            list.add("2023004466");
            list.add("10004516");
            list.add("137");

        } else {
            //小哥哥，女生去搭讪
            list.add("2023004458");
            list.add("10004557");
            list.add("10004558");
            list.add("10004559");
        }

        Collections.shuffle(list);

        List<String> finalList = list.stream().limit(3).collect(Collectors.toList());
        for (String str : finalList) {
            huanxinService.sendTextToUser(str, username, getAccostMsg(sex));
        }

    }


    private String getAccostMsg(Integer sex) {
        List<String> list = Lists.newLinkedList();
        if (sex == 2) {
            list.add("你好呀小姐姐, 可以认识一下嘛？");
            list.add("春风十里，不如遇见你");
            list.add("新来的吗小姐姐，我教你怎么玩嘛？");
            list.add("哈喽~ 你好呀小姐姐");
            list.add("你好呀，小姐姐，你是哪里的呀~");
        } else {
            list.add("你好呀小哥哥, 可以认识一下嘛？");
            list.add("在干嘛呢哥哥？");
            list.add("新来的吗小哥哥，有女朋友吗？");
            list.add("嘤嘤嘤~");
            list.add("捕捉到一只新手小帅哥 我要准备拿捏他~");
        }

        Collections.shuffle(list);
        return list.get(0);
    }


    @Override
    public User saveUserByWx(String name, String unionId, String password, String avatar) {
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(User::getWxUnionId, unionId);
        User ktUser = baseMapper.selectOne(wrapper);
        if (ktUser == null) {
            String username = getUsername();
            User user = new User();
            user.setNickname(name);
            user.setAvatar(avatar);
            LocalDate date = LocalDate.of(2005, 05, 01);
            user.setBirthday(date);
            user.setUsername(username);
            user.setAccount(username);
            user.setIntro("这个人很懒，什么也没写...");
            user.setPhone("");
            user.setWxUnionId(unionId);
            user.setPassword(Md5Utils.hash(password));
            baseMapper.insert(user);

            UserRank userRank = new UserRank();
            userRank.setUserId(user.getId());
            userRankService.save(userRank);

            huanxinService.register(username, Md5Utils.hash(password), username);
            return user;
        }
        return ktUser;
    }

    @Override
    public void cancelAccount(Long userId) {
        User user = baseMapper.selectById(userId);
//        huanxinService.deleteUser(user.getUsername());
//        this.removeById(userId);

        ChatUserVO vo = new ChatUserVO();
        vo.setNickName("该用户已注销！");
        vo.setUserId(userId);
        huanxinService.updateUser(vo,user.getUsername());
        user.setNickname("该用户已注销！");
        this.updateById(user);
    }

    @Override
    public UserVO getUserVO(Long mailUserId) {
        User user = this.getById(mailUserId);
        if (user == null) {
            return null;
        }
        UserVO vo = new UserVO();
        vo.setSex(user.getSex());
        vo.setAvatar(user.getAvatar());
        vo.setBirthday(user.getBirthday());
        vo.setNickname(user.getNickname());
        vo.setId(user.getId());
        return vo;
    }

    @Override
    public List<UserVO> getUserVOList(List<Long> mailUserIds) {
        List<UserVO> list = Lists.newLinkedList();
        if (CollectionUtil.isEmpty(mailUserIds)) {
            return Lists.newLinkedList();
        }
        List<User> users = this.listByIds(mailUserIds);
        for (User user : users) {
            UserVO vo = new UserVO();
            vo.setSex(user.getSex());
            vo.setAvatar(user.getAvatar());
            vo.setBirthday(user.getBirthday());
            vo.setNickname(user.getNickname());
            vo.setId(user.getId());
            list.add(vo);
        }
        return list;
    }

    @Override
    public List<UserVO> getUsersByUsernames(List<String> usernames) {
        List<UserVO> list = Lists.newArrayList();
        if (CollectionUtil.isEmpty(usernames)) {
            return list;
        }
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        wrapper.in(User::getUsername, usernames)
                .last("limit 3");
        List<User> users = this.list(wrapper);
        for (User user : users) {
            UserVO vo = new UserVO();
            vo.setSex(user.getSex());
            vo.setAvatar(user.getAvatar());
            vo.setBirthday(user.getBirthday());
            vo.setNickname(user.getNickname());
            vo.setUsername(user.getUsername());
            vo.setId(user.getId());
            list.add(vo);
        }
        return list;
    }

    @Override
    public List<UserVO> getUserVOListByUsername(List<Integer> usernames) {
        List<UserVO> list = Lists.newArrayList();
        if (CollectionUtil.isEmpty(usernames)) {
            return list;
        }
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        wrapper.in(User::getUsername, usernames)
                .last("limit 3");
        List<User> users = this.list(wrapper);
        for (User user : users) {
            UserVO vo = new UserVO();
            vo.setSex(user.getSex());
            vo.setAvatar(user.getAvatar());
            vo.setBirthday(user.getBirthday());
            vo.setNickname(user.getNickname());
            vo.setId(user.getId());
            list.add(vo);
        }
        return list;
    }


    @Override
    public User getByUsername(String username) {
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(User::getUsername, username);
        return this.getOne(wrapper, false);
    }

    @Override
    public void disturbCircle(Long userId, SetDisturbDTO dto) {
        userDisturbService.disturbCircle(userId, dto);
    }

    @Override
    public List<Long> getRobotIds() {
        QueryWrapper<User> query = Wrappers.query();
        query.select("id").eq("is_robot", 1).orderByAsc("RAND()");
        IPage<User> page = baseMapper.selectPage(new Page().setSize(10), query);
        return page.getRecords().stream().map(User::getId).collect(Collectors.toList());
    }

    @Override
    public IPage<User> getUserList(IPage<User> page, Date beginTime, Date endTime) {
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        wrapper.ge(User::getCreateTime, beginTime);
        wrapper.lt(User::getCreateTime, endTime);
        return baseMapper.selectPage(page, wrapper);
    }

    @Override
    public Boolean getUserExistByPhone(String phone) {
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(User::getPhone, phone);

        Long count = baseMapper.selectCount(wrapper);
        return count > 0L;
    }

    @Override
    public User saveUserByApple(String appleId, String password) {
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(User::getAppleId, appleId);
        User ktUser = baseMapper.selectOne(wrapper);
        if (ktUser == null) {
            String username = getUsername();
            User user = new User();
            Integer count = baseMapper.getUserCount();
//            user.setNickname(username);
            user.setUsername(username);
            user.setAccount(username);
            user.setIntro("这个人很懒，什么也没写...");
            user.setPhone("");
            user.setAppleId(appleId);
            user.setPassword(Md5Utils.hash(password));
            baseMapper.insert(user);
            huanxinService.register(username, Md5Utils.hash(password), username);
            return user;
        }
        return ktUser;
    }


    public String getUsername() {
        Integer count = baseMapper.getUserCount();
        count++;
        return count.toString();
    }


    @Override
    public List<RecommendUserVO> getRecommendUsers(Long userId, Double userLng, Double userLat) {

        User currentUser = this.getById(userId);
        Integer isOpenRecommend = currentUser.getIsOpenRecommend();//是否开启个性化推荐

        //获取在线用户
        //todo 后面需要自己实现 socket 来获取在线用户列表
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(User::getIsRobot, 0)
                .ne(User::getId, userId)
                .orderByDesc(User::getUpdateTime)
                .last("limit 200");
        List<User> allUserList = this.list(wrapper);
        List<List<User>> partition = Lists.partition(allUserList, 100);
        List<User> onlineUsers = Lists.newLinkedList();
        for (List<User> users : partition) {
            List<String> usernames = users.stream().map(User::getUsername).collect(Collectors.toList());
            if (CollectionUtil.isEmpty(usernames)){
                break;
            }
            JSONArray statusList = huanxinService.batchUserStatus(usernames);
            //处理用户在线状态
            for (Object obj : statusList) {
                String str = JSONObject.toJSONString(obj);
                JSONObject jsonObject = JSONObject.parseObject(str);
                Set<Map.Entry<String, Object>> entries = jsonObject.entrySet();
                for (Map.Entry<String, Object> entry : entries) {
                    String value = entry.getValue().toString();
                    Optional<User> opt = users.stream().filter(u -> u.getUsername().equals(entry.getKey())).findFirst();
                    if (opt.isPresent() && opt.get().getUsername().equals(entry.getKey()) && "online".equals(entry.getValue())) {
                        onlineUsers.add(opt.get());
                        break;
                    }
                }
            }

        }
        onlineUsers = onlineUsers.stream().limit(20).collect(Collectors.toList());
        //优先获取附近的人，如果没有，则随机获取。
        LinkedList<RecommendUserVO> finalList = Lists.newLinkedList();

        for (User onlineUser : onlineUsers) {
            RecommendUserVO vo = convertToRecommendUser(onlineUser);
            finalList.add(vo);
        }

        if (null != isOpenRecommend && isOpenRecommend == 0) { //如果关闭了个性化推荐，直接跳过。
            this.patchRecommendUsers(finalList);
            return finalList;
        }

        Integer recommendSex = UserConstance.SEX_FEMALE;
        if (UserConstance.SEX_FEMALE.equals(currentUser.getSex())) {
            recommendSex = UserConstance.SEX_MALE;
        }

        List<Long> onlineUserIds = finalList.stream().map(RecommendUserVO::getId).collect(Collectors.toList());

        wrapper.clear();
        if (CollectionUtil.isNotEmpty(onlineUserIds)){
            wrapper.notIn(User::getId, onlineUserIds);
        }

        wrapper.eq(User::getIsOpenPhoto, true)
                .ne(User::getId, userId)
                .eq(User::getSex, recommendSex)
                .orderByDesc(User::getCreateTime)
                .last("limit 100");

        List<User> havePhotoUsers = this.list(wrapper);
        Collections.shuffle(havePhotoUsers);

        for (User user : havePhotoUsers) {
            RecommendUserVO userVO = convertToRecommendUser(user);
            finalList.add(userVO);
            if (finalList.size() >= 20) {
                this.patchRecommendUsers(finalList);
                return finalList;
            }
        }

//        List<Long> onlineIds = finalList.stream().map(RecommendUserVO::getId).collect(Collectors.toList());
//        if (userLng != null && userLat != null){
//            List<NearbyUserVO> nearUsers = nearbyUserService.nearBySearch(50d, userLng, userLat);
//            Collections.shuffle(nearUsers); //打乱顺序
//            List<NearbyUserVO> list = nearUsers.stream().filter(l->!onlineIds.contains(l.getUserId())).limit(20).collect(Collectors.toList());
//            List<Long> nearUserIds = list.stream().map(NearbyUserVO::getUserId).collect(Collectors.toList());
//            if (CollectionUtil.isNotEmpty(nearUserIds)){
//                wrapper.clear();
//                wrapper.eq(User::getSex, recommendSex)
//                        .in(User::getId, nearUserIds);
//                List<User> users = this.list(wrapper);
//                for (User user : users) {
//                    RecommendUserVO userVO = convertToRecommendUser(user);
//                    Optional<NearbyUserVO> optional = list.stream().filter(l -> l.getUserId().equals(user.getId())).findAny();
//                    if (optional.isPresent()){
//                        userVO.setDistant(optional.get().getDistance());
//                    }
//                    finalList.add(userVO);
//
//                    if (finalList.size()>=20){
//                        Collections.shuffle(finalList);
//                        this.patchRecommendUsers(finalList);
//                        return finalList;
//                    }
//                }
//            }
//        }

        Collections.shuffle(finalList);


        this.patchRecommendUsers(finalList);

        return finalList;
    }

    @Override
    public void createVoiceCard(Long userId, String voiceUrl, String imgUrl) {
        User user = this.getById(userId);
        user.setVoice(voiceUrl);
        user.setVoiceImg(imgUrl);
        this.updateById(user);
    }

    @Override
    public List<UserDTO> getVoiceMatchList(Long userId) {
        //获取匹配过的用户
        LambdaQueryWrapper<UserMatch> userMatchWrapper = Wrappers.lambdaQuery();
        userMatchWrapper.eq(UserMatch::getUserId, userId);
        //匹配过的用户id集合
        List<Long> matchUserIds = userMatchService.list(userMatchWrapper).stream().map(UserMatch::getMatchUserId).collect(Collectors.toList());

        //获取当前用户
        User currentUser = this.getById(userId);
        Integer sex = currentUser.getSex();

        //获取异性且有声音卡片的用户
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();

        if (UserConstance.SEX_FEMALE.equals(sex)) {
            //女性，匹配男
            wrapper.eq(User::getSex, UserConstance.SEX_MALE);
        } else {
            wrapper.eq(User::getSex, UserConstance.SEX_FEMALE);
        }

        //过滤掉匹配过的用户
        if (CollectionUtil.isNotEmpty(matchUserIds)) {
            wrapper.notIn(User::getId, matchUserIds);
        }

        wrapper.eq(User::getIsRobot,0)
                .isNotNull(User::getVoice)
                .orderByDesc(User::getCreateTime)
                .last("limit 20");
        List<User> users = this.list(wrapper);
        List<UserDTO> userDTOS = UserConverter.toDtoList(users);
        List<String> usernames = userDTOS.stream().map(UserDTO::getUsername).collect(Collectors.toList());
        for (UserDTO userDTO : userDTOS) {
            List<String> tagList = getTagList(userDTO.getTagIds());
            userDTO.setTags(tagList);
        }

        JSONArray statusList = huanxinService.batchUserStatus(usernames);


        return userDTOS;
    }


    @Override
    public void subBalance(Long userId, BigDecimal number) {
        User user = this.getById(userId);
        BigDecimal balance = user.getBalance();
        if (balance.doubleValue() < number.doubleValue()) {
            throw new ServiceException("余额不足！");
        }
        BigDecimal finalBalance = balance.subtract(number).setScale(2, RoundingMode.HALF_UP);
        User updateUser = new User();
        updateUser.setId(userId);
        updateUser.setBalance(finalBalance);
        this.updateById(updateUser);
    }

    @Override
    public void addBalance(Long userId, BigDecimal number) {
        User user = this.getById(userId);
        BigDecimal balance = user.getBalance();
        BigDecimal finalBalance = balance.add(number).setScale(2, RoundingMode.HALF_UP);
        User updateUser = new User();
        updateUser.setId(userId);
        updateUser.setBalance(finalBalance);
        this.updateById(updateUser);
    }

    @Override
    public void increaseProfit(Long userId, BigDecimal number) {
        User user = this.getById(userId);
        BigDecimal totalProfit = user.getTotalProfit();
        BigDecimal currentProfit = totalProfit.add(number);

        User updateUser = new User();
        updateUser.setId(userId);
        updateUser.setTotalProfit(currentProfit);
        this.updateById(updateUser);
    }

    @Override
    public void subProfit(Long userId, BigDecimal number) {
        User user = this.getById(userId);
        BigDecimal totalProfit = user.getTotalProfit();
        BigDecimal currentProfit = totalProfit.subtract(number);
        User updateUser = new User();
        updateUser.setId(userId);
        updateUser.setTotalProfit(currentProfit);
        this.updateById(updateUser);
    }

    @Override
    public List<User> batchByUsernames(List<String> usernames) {
        if (CollectionUtil.isEmpty(usernames)){
            return Lists.newArrayList();
        }
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        wrapper.in(User::getUsername, usernames);
        List<User> list = this.list(wrapper);
        return list;
    }

    @Override
    public void subBalance(Long userId, Integer price) {
        User user = this.getById(userId);
        BigDecimal balance = user.getBalance();

        if (balance.intValue() < price){
            throw new ServiceException("余额不足！");
        }
        balance = balance.subtract(BigDecimal.valueOf(price)).setScale(2, RoundingMode.HALF_UP);
        user.setBalance(balance);
        this.updateById(user);
    }

    @Override
    public IPage<SubordinateUserVO> getSubordinateUsers(String phone, Integer page, Integer size) {

        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(User::getPhone,phone);
        User currentUser = this.getOne(wrapper);

        wrapper.clear();
        wrapper.eq(User::getParentUserId,currentUser.getId());
        //获取下级用户列表
        IPage<User> subordinateUsrs = this.page(new Page<>(page, size),wrapper);

        List<SubordinateUserVO> finalList = Lists.newLinkedList();
        for (User user : subordinateUsrs.getRecords()) {
            SubordinateUserVO vo = new SubordinateUserVO();
            vo.setAvatar(user.getAvatar());
            vo.setNickname(user.getNickname());
            vo.setCreateTime(user.getCreateTime());
            vo.setUserId(user.getId());
            LocalDateTime createTime = user.getCreateTime();
            LocalDateTime now = LocalDateTime.now();
            int daysNum=(int)(now.toLocalDate().toEpochDay() - createTime.toLocalDate().toEpochDay());
            vo.setDayNum(daysNum);
            //获取总充值金额
            Double totalRecharge = orderService.getTotalRecharge(user.getId());
            vo.setRechargeNumber(totalRecharge);
            vo.setRate("3%");
            vo.setCommission(totalRecharge*0.03);
            finalList.add(vo);
        }

        IPage<SubordinateUserVO> result = new Page<>();
        result.setRecords(finalList);
        result.setTotal(subordinateUsrs.getTotal());

        return result;
    }

    @Override
    public Double getTotalCommission(String phone) {
        //获取所有下级用户
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(User::getPhone,phone);
        User currentUser = this.getOne(wrapper);
        //下级用户所有订单
        wrapper.clear();
        wrapper.eq(User::getParentUserId, currentUser.getId());
        List<Long> userIds = this.list(wrapper).stream().map(User::getId).collect(Collectors.toList());

        Double totalRecharge = orderService.getTotalRecharge(userIds);

        return totalRecharge * 0.08;
    }


    public static void main(String[] args) {
        LocalDateTime now = LocalDateTime.now();
        // 周一
        LocalDateTime monday = now.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
        // 周日
        LocalDateTime sunday = now.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));

        LocalDateTime weekStart = LocalDateTime.of(monday.toLocalDate(), LocalTime.MIN);
        LocalDateTime weekLast = LocalDateTime.of(sunday.toLocalDate(), LocalTime.MAX);

        System.out.println(weekStart);
        System.out.println(weekLast);
    }

    @Override
    @Transactional(rollbackFor = ServiceException.class)
    public void saveAddress(Long userId, String add) {
        if (StringUtils.isBlank(add)){
            return;
        }
        User user = this.getById(userId);
        JSONObject jsonObject = JSONObject.parseObject(add);
        String info = jsonObject.getString("info");
        if ("OK".equals(info)){
            JSONObject regeocode = jsonObject.getJSONObject("regeocode");
            String address = regeocode.getString("formatted_address");
            user.setAddress(address);
            JSONObject addressComponent = regeocode.getJSONObject("addressComponent");
            String province = addressComponent.getString("province");
            String district = addressComponent.getString("district");
            user.setProvince(province);
            user.setDistrict(district);

            String addressStr = "";
            if ("[]".equals(district)||StringUtils.isBlank(district)){
                addressStr = province;
            }else {
                addressStr = province + "-" + district;
            }

            userSkillService.updateAddress(userId, addressStr);
            this.updateById(user);
        }
    }

    @Override
    public void bindUser(String phone, String defaultPassword,Long parentUserId) {
        this.saveUser(phone,defaultPassword,parentUserId);
    }

    /**
     * 获取子用户列表
     * @param parentUserId
     * @return
     */
    @Override
    public List<ShareUserVO> getByParentId(Long parentUserId, Integer page, Integer size) {
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(User::getParentUserId, parentUserId);
        List<User> list = this.page(new Page<>(page, size), wrapper).getRecords();

        List<ShareUserVO> finalList = Lists.newLinkedList();

        for (User user : list) {
            ShareUserVO vo = new ShareUserVO();
            LocalDateTime createTime = user.getCreateTime();
            LocalDateTime now = LocalDateTime.now();
            int daysNum=(int)(now.toLocalDate().toEpochDay() - createTime.toLocalDate().toEpochDay());
            vo.setDays(daysNum);
            vo.setProfit(0d);
            vo.setRate("3%");
            vo.setAvatar(user.getAvatar());
            vo.setNickname(user.getNickname());

            finalList.add(vo);
        }

        return finalList;
    }

    @Override
    public Long getSubordinateCount(Long userId) {
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(User::getParentUserId, userId);

        return this.count(wrapper);
    }

    @Override
    public IPage<User> searchUser(Integer page, Integer size, String keyword) {
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        if (StringUtils.isNotBlank(keyword)){
            wrapper.and(w->w.like(User::getNickname,keyword).or()
                    .like(User::getUsername,keyword).or()
                    .like(User::getPhone, keyword));
        }
        wrapper.orderByDesc(User::getCreateTime);
        Page<User> result = this.page(new Page<>(page, size),wrapper);
        return result;
    }

    @Override
    public User getByInviteCode(String inviteCode) {
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(User::getInviteCode, inviteCode);
        return this.getOne(wrapper);
    }

    @Override
    public void bindUserByInviteCode(Long currentUserId, String inviteCode) {

        log.info("开始绑定账号，code:{}",inviteCode);
        if (StringUtils.isBlank(inviteCode)){
            return;
        }


        User currentUser = this.getById(currentUserId);
        if (currentUser.getParentUserId() != null){
            throw new ServiceException("你的账号已经绑定邀请码，请勿重复绑定！");
        }
        if (inviteCode.equals(currentUser.getInviteCode())){
            return;
        }
        User user = this.getByInviteCode(inviteCode);
        log.info("查询上级，user:{}",user);
        if (null == user){
            throw new ServiceException("您输入的邀请码不存在！");
        }
        currentUser.setParentUserId(user.getId());

        this.updateById(currentUser);
    }

    @Override
    public List<User> getFindObjectUserList(String addr, Integer page, Integer size) {
        if ("全国".equals(addr)){
            return this.page(new Page<>(page,size)).getRecords();
        }else {
            LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
            wrapper.like(User::getAddress, addr);

            return this.page(new Page<>(page,size),wrapper).getRecords();
        }

    }

    @Override
    public User getByAccount(String account) {
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(User::getAccount, account);
        return this.getOne(wrapper);
    }

    @Override
    public void updateBalance(UpdateUserNumberDTO dto) {
        Integer type = dto.getType();
        if (type == null || type == 0){
            throw new ServiceException("请输入修改类型！");
        }

        Long userId = dto.getUserId();
        User user = this.getById(userId);
        if (type == 1){
            this.addBalance(userId,dto.getNumber());
            userBillService.systemEditBalance(userId, dto.getNumber(), dto.getType());
        }else if (type == -1){
            this.subBalance(userId, dto.getNumber());
            userBillService.systemEditBalance(userId, dto.getNumber(), dto.getType());
        }
    }

    @Override
    public void updateProfit(UpdateUserNumberDTO dto) {
        Integer type = dto.getType();
        if (type == null || type == 0){
            throw new ServiceException("请输入修改类型！");
        }
        Long userId = dto.getUserId();
        User user = this.getById(userId);
        if (type == 1){
            this.increaseProfit(userId,dto.getNumber());
            userBillService.systemEditProfit(userId, dto.getNumber(), dto.getType());
        }else if (type == -1){
            this.subProfit(userId, dto.getNumber());
            userBillService.systemEditProfit(userId, dto.getNumber(), dto.getType());
        }
    }

    @Override
    public User getByPhone(String phone) {
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(User::getPhone, phone);
        return this.getOne(wrapper,false);
    }

    @Override
    public void updateUserAccount(UpdateUserAccountDTO dto) {
        Long userId = dto.getUserId();
        String account = dto.getAccount();
        User existUser = this.getByAccount(account);
        if (existUser != null){
            throw new ServiceException("该账号已存在！");
        }

        User user = this.getById(userId);
        if (dto.getIsBeautifulAccount() != null && dto.getAccount() != null){
            if (dto.getIsBeautifulAccount()==1 && dto.getAccount().length()!=5){
                throw new ServiceException("靓号必须是五位数！");
            }
            Integer isGiveBalance = dto.getIsGiveBalance();
            user.setAccount(account);
            user.setIsBeautifulId(dto.getIsBeautifulAccount());
            this.updateById(user);
        }
        if (dto.getIsGiveBalance() != null){
            user.setIsGiveBalance(dto.getIsGiveBalance());
            this.updateById(user);
        }
    }

    @Override
    public List<User> listByUsernames(List<String> usernames) {
        if (usernames.isEmpty()){
            return Lists.newArrayList();
        }
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        wrapper.in(User::getUsername,usernames);

        return this.list(wrapper);
    }

    @Override
    public void subAdAstraBalance(Long userId, int totalCount) {
        User user = this.getById(userId);
        Integer adAstraBalance = user.getAdAstraBalance();
        //扣减数量
        adAstraBalance -= totalCount;
        if (adAstraBalance<0){
            throw new ServiceException("可派遣飞机数量不足！");
        }
        user.setAdAstraBalance(adAstraBalance);
        this.updateById(user);
    }

    @Override
    public Double getWeekCommission(String phone) {

        //获取所有下级用户
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(User::getPhone,phone);
        User currentUser = this.getOne(wrapper);
        LocalDateTime now = LocalDateTime.now();

        // 周一
        LocalDateTime monday = now.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
        // 周日
        LocalDateTime sunday = now.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));

        LocalDateTime weekStart = LocalDateTime.of(monday.toLocalDate(), LocalTime.MIN);
        LocalDateTime weekLast = LocalDateTime.of(sunday.toLocalDate(), LocalTime.MAX);
        //下级用户所有订单
        wrapper.clear();
        wrapper.eq(User::getParentUserId, currentUser.getId())
                .between(User::getCreateTime,weekStart,weekLast);
        List<Long> userIds = this.list(wrapper).stream().map(User::getId).collect(Collectors.toList());

        Double totalRecharge = orderService.getTotalRecharge(userIds);

        return totalRecharge * 0.08;
    }




    public void patchRecommendUsers(List<RecommendUserVO> recommendUsers) {
        if (recommendUsers.isEmpty()) {
            return;
        }
        List<Long> userIds = recommendUsers.stream().map(RecommendUserVO::getId).collect(Collectors.toList());
        List<String> usernames = recommendUsers.stream().map(RecommendUserVO::getUsername).collect(Collectors.toList());
        JSONArray statusList = huanxinService.batchUserStatus(usernames);
        List<Dynamic> dynamicList = dynamicMapper.getRecommendUserDynamic(userIds);

        List<PartyUser> partyUsers = partyUserService.getByUserIds(userIds);

        LambdaQueryWrapper<UserPhoto> photoWrapper = Wrappers.lambdaQuery();
        photoWrapper.in(UserPhoto::getUserId, userIds);
        List<UserPhoto> photoList = userPhotoService.list(photoWrapper);

        recommendUsers.forEach(ru -> {
            //处理用户语聊状态
            for (PartyUser partyUser : partyUsers) {
                if (ru.getId().equals(partyUser.getUserId())) {
                    //再校验一次
                    ChannelUserVO vo = agoraClient.getUsers(partyUser.getPartyId()).toJavaObject(ChannelUserVO.class);
                    if (vo.getChannelExist()) {
                        List<String> broadcaseters = vo.getBroadcasters().stream().map(l -> l.toString()).collect(Collectors.toList());
                        List<String> audiences = vo.getAudience().stream().map(l -> l.toString()).collect(Collectors.toList());
                        LinkedList<String> allUsernames = Lists.newLinkedList();
                        allUsernames.addAll(broadcaseters);
                        allUsernames.addAll(audiences);
                        if (allUsernames.contains(ru.getUsername())) {
                            ru.setLiveStatus(UserLiveStatusEnum.VOICE_LIVE.getStatus());
                            ru.setPartyId(partyUser.getPartyId());
                        }
                    }

                    break;
                }
            }
            //处理用户在线状态
//            for (Object obj : statusList) {
//                String str = JSONObject.toJSONString(obj);
//                JSONObject jsonObject = JSONObject.parseObject(str);
//                Set<Map.Entry<String, Object>> entries = jsonObject.entrySet();
//                for (Map.Entry<String, Object> entry : entries) {
//                    String value = entry.getValue().toString();
//                    if (ru.getUsername().equals(entry.getKey())) {
//                        ru.setOnlineStatus("online".equals(value) ? 1 : 0);
//                        break;
//                    }
//                }
//            }
            ru.setOnlineStatus(1); //todo 暂时先都为在线
            //处理图片
            List<Dynamic> dynamics = dynamicList.stream().filter(dynamic -> dynamic.getUserId().equals(ru.getId())).collect(Collectors.toList());
            List<String> imgList = dynamics.stream().map(Dynamic::getImgUrls).collect(Collectors.toList());
            List<String> ruImgs = Lists.newLinkedList();
            List<String> photoUrls = photoList.stream().filter(p -> p.getUserId().equals(ru.getId())).map(UserPhoto::getPhotoUrl).collect(Collectors.toList());

            photoUrls.addAll(imgList);
            for (String imgUrls : photoUrls) {
                if (ruImgs.size() >= 3) {
                    break;
                }
                List<String> imgs = Arrays.asList(imgUrls.split(","));
                for (String img : imgs) {
                    if (ruImgs.size() >= 3) {
                        break;
                    }
                    ruImgs.add(img);
                }
            }
            //处理用户图片列表。
            ru.setImgList(ruImgs);
        });

    }





    private RecommendUserVO convertToRecommendUser(User user) {
        RecommendUserVO vo = new RecommendUserVO();
        BeanUtils.copyProperties(user, vo);
        vo.setVoiceUrl(user.getVoice());
        vo.setLiveStatus(UserLiveStatusEnum.NO_LIVE.getStatus());
        List<String> tagList = getTagList(user.getTagIds());
        vo.setTagList(tagList);
        return vo;
    }

}
